/*
 * Scanner.cpp
 *
 *  Created on: Apr 8, 2010
 *      Author: Stefan Obermeier
 */

#include "../scanner/Scanner.h"
#include "../buffer/buffer.h"
#include "../token/token.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <errno.h>
#include <limits.h>

using namespace std;

/*
 * Liest so lange Zeichen bis ein Token entstanden ist
 */
token* Scanner::nextToken() {

	zustand = 0;
	zustand = verarbeiteNextWord();

	// Innerhalb eines Kommentars werden alle Zeichen akzeptiert
	while (zustand == 14 | zustand == 15) {

		// Wenn der Kommentar bis zum Ende der Datei nicht abgeschlossen wurde gibt es einen Fehler
		if (buffer->endOfFileNear) {
			berechneColumn();
			ergebnisToken = new token(TOKEN_COMMENT_NOT_CLOSED, line,
					columnBerechnet);
			z0Konfiguration();
			return ergebnisToken;
		} else {
			zustand = verarbeiteNextWord();
		}
	}

	switch (zustand) {

	case 0:
		wortZuEnde = false;
		zustand = 0;
		laengenZaehler = 0;
		anzahlLeerzeichen = 0;
		return 0;
		break;
	case 1:
	case 2:
	case 3:
	case 4:
	case 6:
	case 7:
	case 8:

		berechneColumn();
		ergebnisToken = new token(TOKEN_IDENTYFIER, line, columnBerechnet + 1,
				LexemSpeicher);
		z0Konfiguration();

		return ergebnisToken;

	case 5:

		berechneColumn();
		ergebnisToken = new token(TOKEN_PRINT, line, columnBerechnet + 1);
		z0Konfiguration();

		return ergebnisToken;

	case 9:

		berechneColumn();
		ergebnisToken = new token(TOKEN_IDENTYFIER, line, columnBerechnet + 1,
				LexemSpeicher);
		z0Konfiguration();

		return ergebnisToken;

	case 10:

		berechneColumn();
		ergebnisToken = new token(TOKEN_READ, line, columnBerechnet + 1);
		z0Konfiguration();

		return ergebnisToken;

	case 11:

		long wert;
		columnBerechnet = (column - anzahlLeerzeichen - (laengenZaehler - 2));
		//		cout << "Column  " << column << "  leerzeichen " << this->anzahlLeerzeichen  << " lanege " << laengenZaehler << endl;
		errno = 0;
		wert = strtol(LexemSpeicher, NULL, 10);
		ergebnisToken = new token(TOKEN_INTEGER, line, columnBerechnet, wert);

		// Ueberlauf pruefen (man page)
		if ((errno == ERANGE && (wert == LONG_MAX || wert == LONG_MIN))
				|| (errno != 0 && wert == 0)) {
			perror("strtol");
			//     exit(EXIT_FAILURE);
		}

		z0Konfiguration();

		return ergebnisToken;

	case 13:

		berechneColumn();
		ergebnisToken = new token(TOKEN_BRACE_OPEN, line, columnBerechnet + 1);
		z0Konfiguration();

		return ergebnisToken;

	case 17:

		berechneColumn();
		ergebnisToken = new token(TOKEN_LESS, line, columnBerechnet + 1);
		z0Konfiguration();
		return ergebnisToken;

	case 18:

		berechneColumn();
		ergebnisToken = new token(TOKEN_EQUAL, line, columnBerechnet + 1);
		z0Konfiguration();
		return ergebnisToken;

	case 19:

		berechneColumn();
		ergebnisToken = new token(TOKEN_FUCHS, line, columnBerechnet + 1);
		z0Konfiguration();
		return ergebnisToken;

	case 16:

		z0Konfiguration();
		return 0; // Noetig falls mit dem letzten Zeichen der Kommentar abgeschlossen wird.
		break;

	case 20:

		berechneColumn();
		columnBerechnet = (column - anzahlLeerzeichen - (laengenZaehler - 2));

		// cout << "Column  " << column << "  leerzeichen " << this->anzahlLeerzeichen  << " lanege " << laengenZaehler << endl;

		ergebnisToken = new token(TOKEN_ERROR, line, columnBerechnet,
				LexemSpeicher);
		z0Konfiguration();
		return ergebnisToken;

	case 21:

		berechneColumn();
		ergebnisToken = new token(TOKEN_PLUS, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 22:

		berechneColumn();
		ergebnisToken = new token(TOKEN_MINUS, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 23:

		berechneColumn();
		ergebnisToken = new token(TOKEN_MULTIPLIE, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 24:

		berechneColumn();
		ergebnisToken = new token(TOKEN_DIVIDE, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 25:

		berechneColumn();
		ergebnisToken = new token(TOKEN_SEMICOLON, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 26:

		berechneColumn();
		ergebnisToken = new token(TOKEN_BRACE_OPEN, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 27:

		berechneColumn();
		ergebnisToken = new token(TOKEN_BRACE_CLOSE, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 28:

		berechneColumn();
		ergebnisToken = new token(TOKEN_SQUARED_BRACKET_OPEN, line,
				columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 29:

		berechneColumn();
		ergebnisToken = new token(TOKEN_SQUARED_BRACKET_CLOSE, line,
				columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 30:

		berechneColumn();
		ergebnisToken = new token(TOKEN_AND, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 31:

		berechneColumn();
		ergebnisToken = new token(TOKEN_LESS, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 32:

		berechneColumn();
		ergebnisToken = new token(TOKEN_GREATER, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 33:

		berechneColumn();
		ergebnisToken = new token(TOKEN_EQUAL, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 34:

		berechneColumn();
		ergebnisToken = new token(TOKEN_FACTORIAL, line, columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 35:

		berechneColumn();
		ergebnisToken = new token(TOKEN_CURLY_BRACKET_OPEN, line,
				columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	case 36:

		berechneColumn();
		ergebnisToken = new token(TOKEN_CURLY_BRACKET_CLOSE, line,
				columnBerechnet);
		z0Konfiguration();
		return ergebnisToken;

	default:

		cout << "Programmierfehler Automat an undefiniertem Zustand verlassen "
				<< zustand << endl;

		;

	}

}

int Scanner::verarbeiteNextWord() {
	wortZuEnde = false;

	while ((!wortZuEnde) & !(buffer->endOfFileNear)) {

		inKommentar = (zustand == 14 | zustand == 15);

		// Falls man in einer neuen Zeile ist wird der Spaltenzaehler zurueckgesetzt
		if (neueZreile) {
			neueZreile = false;
			line++;
			column = 0;
		}

		// Hole neue Zeichen
		try {
			aktuellesZeichen = buffer->getChar();
			column++;
			// Inhalt eines Kommentars soll nicht im Lexem auftauchen
			if (!inKommentar) {
				laengenZaehler++;
			} else {
				anzahlLeerzeichen++;
			}

		} catch (noMoreChar_exception) {
			wortZuEnde = true;
			buffer->endOfFileNear = true;
			//cout << "Es gibt keine Zeichen mehr" << endl;
			// throw noMoreChar_exception();
		}

		if (!inKommentar) {

			// Erkennt das Wortende
			try {

				// Wort endet mit Leerzeichen oder Zeilenumbruech. Wenn es mehrere gibt werden sie entfernt.
				while (((aktuellesZeichen == ' ') | (aktuellesZeichen == '\n'))
						& !buffer->endOfFileNear) {

					// Zeilenzaehler
					if (aktuellesZeichen == '\n') {
						neueZreile = true;

					}

					aktuellesZeichen = buffer->getChar();
					// laengenZaehler++;
					column++;
					anzahlLeerzeichen++;

					wortZuEnde = true;
				}

				//Wort endet nach Sonderzeichen
				if (((((int) aktuellesZeichen > 64) & ((int) aktuellesZeichen
						< 91)) | (((int) aktuellesZeichen > 96)
						& ((int) aktuellesZeichen < 123))
						| (((int) aktuellesZeichen > 48)
								& ((int) aktuellesZeichen < 58))) & ((zustand
						== 12) | (zustand == 13) | (zustand == 17) | (zustand
						== 18 | zustand == 20 | zustand == 16))) {

					wortZuEnde = true;

				}

				//Ende einer Zahl und beginn eines Bezeichnesrs 234ba
				if (((((int) aktuellesZeichen > 64) & ((int) aktuellesZeichen
						< 91)) | (((int) aktuellesZeichen > 96)
						& ((int) aktuellesZeichen < 123))) & zustand == 11) {
					signWortende();
				}

				// Wortende bei Sonderzeichen
				switch ((int) aktuellesZeichen) {
				// "+-/*>=!&;){}[]<("

				case (int) ';':
					signWortende();
					break;
				case (int) '+':
					signWortende();
					break;

				case (int) '-':
					signWortende();
					break;

				case (int) '/':
					signWortende();
					break;

				case (int) '*':
					if (zustand == 13) {
						break; // Kommentarbeginn
					} else {
						signWortende();
						break;
					}
				case (int) '>': // Ende von <=>
					if (zustand == 18) {
						break;
					} else {
						signWortende();
						break;
					}

				case (int) '<':
					if ((int) buffer->getChar() == '=') {

						if (zustand == 0) { // Start von <=>
							buffer->ungetChar();
							break;
						} else { // Erst das Letzte Wort beenden
							buffer->ungetChar();
							signWortende();
							break;
						}
					} else {
						buffer->ungetChar();
						signWortende();
						break;
					}

				case (int) '=':
					if (zustand == 17 & (buffer->getChar() == '>')) { // Ist <=>
						buffer->ungetChar();
						break;
					} else { // <= ergibt 2 Tokens
						buffer->ungetChar();
						signWortende();
					}

				case (int) '!':
					signWortende();
					break;
				case (int) '&':
					signWortende();
					break;
				case (int) ')':
					signWortende();
					break; // Abgedeckt durch Abfrage imKommentar
				case (int) '{':
					signWortende();
					break;
				case (int) '}':
					signWortende();
					break;
				case (int) ']':
					signWortende();
					break;
				case (int) '[':
					signWortende();
					break;
				case (int) '(':
					if ((int) buffer->getChar() == '*') { // Kommentar wird beginnen oder

						if (zustand == 0) {
							buffer->ungetChar();
							// column--;
							break;
						} else {
							buffer->ungetChar();
							signWortende();
							break;
						}

					} else { // ist ein einzelnes Symbol
						buffer->ungetChar();
						signWortende();
						break;
					}
					wortZuEnde = true;
					break;
				}

				// <=><=><=> sind mehrere fuechse
				if (zustand == 19) {
					signWortende();
				}
			} catch (noMoreChar_exception) {
				wortZuEnde = true;
				buffer->endOfFileNear = true;
				// throw noMoreChar_exception();
			}
		} // In Kommentar Ende


		// Zeilenzaehler (Auch im Kommentar werden Zeilen gezaehlt

		if ((aktuellesZeichen == '\n') & (inKommentar)) {

			column = 0;
			line++;
			neueZreile == true;
		}

		if (zustand == 20) { // Fuer jeden Fehler ein Eigenes Token
			signWortende();

		}

		// Uebergebe Zeichen an den Automaten setze Buffer auf das Naechste gueltige Zeichen
		if (wortZuEnde) {
			// Damit das Letzte Zeichen auch geprueft wird
			if (!buffer->endOfFileNear) {
				buffer->ungetChar(); // um auf dem 1. Ziechen des neuen Wortes zu stehen
				column--;
			} else {
			}

		} else {

			// cout << "Bin in  Zustand " << this->zustand << " lese " <<  aktuellesZeichen << endl;
			this->zustand = automat->uebergang(this->zustand, aktuellesZeichen);

			wortZuEnde = false;
		}
	}

	// Lexeme werden gespeichert wenn das Token ein Identifier, Fehler oder Zahl  ist.
	if (zustand == 9 | zustand == 20 | zustand == 11) {
		lexemSpeicher(laengenZaehler);
	}

	return zustand;
}

void Scanner::signWortende() {
	if (zustand == 0) {

		zustand = automat->uebergang(this->zustand, aktuellesZeichen);
		buffer->getChar();
		column++;
		wortZuEnde = true;

		// Inhalt eines Kommentars soll nicht im Lexem auftauchen
		if (!inKommentar) {
			//laengenZaehler++;
		} else {
			anzahlLeerzeichen++;
		}

	} else {
		wortZuEnde = true;

	}

}
Scanner::Scanner(Buffer* buffer) {
	this->buffer = buffer;
	automat = new Automat1();
	line = 1;
	column = 0;
	zahl = 0;
	laengenZaehler = 0;
	anzahlLeerzeichen = 0;
	columnBerechnet = 0;
	wortZuEnde = false;
	inKommentar = false;
	neueZreile = false;
	;
}

void Scanner::lexemSpeicher(int laenge) {

	laenge--;

	if (buffer->endOfFileNear) {
		laenge++;
		// buffer->ungetChar();   // Damit auch das letzte Zeichen kopiert wird.
		//column--;
	}

	// Gehe zum Wortanfang
	for (int i = 0; i < ((laenge) + anzahlLeerzeichen); i++) {
		buffer->ungetChar();
	}

	LexemSpeicher = new char[laenge + 1];

	// Kopiere Wort
	for (int i = 0; i < laenge; i++) {
		LexemSpeicher[i] = buffer->getChar();
		//cout << "Lexeme " << (int) LexemSpeicher[i];

	}
	LexemSpeicher[laenge] = 0;

	// Das naechste Zeichen ist das naechste gueltige Zeichen ( wichtig fuer den Automat)
	for (int i = 0; i < anzahlLeerzeichen; i++) {
		buffer->getChar();
	}

}

void Scanner::z0Konfiguration() {

	wortZuEnde = false;
	zustand = 0;
	wortZuEnde = false;
	anzahlLeerzeichen = 0;
	laengenZaehler = 0;

}

void Scanner::berechneColumn() {

	this->columnBerechnet = (this->column - this->anzahlLeerzeichen
			- (this->laengenZaehler - 1));

	// cout << "Column  " << column << "  leerzeichen " << this->anzahlLeerzeichen  << " lanege " << laengenZaehler << endl;

	if (buffer->endOfFileNear) {
		this->columnBerechnet--;
	}
}
Scanner::~Scanner() {
	// TODO Auto-generated destructor stub
}
